import os
import sys
import time

import cv2
import face_recognition
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject
from PyQt5.QtGui import QImage, QPixmap, QPalette, QColor
from PyQt5.QtWidgets import QApplication, QSizePolicy

from db import EmployeeDB  # 确保导入数据库操作类
# 导入UI类
from main_ui import MainWindow


class FaceRecognitionManager(QObject):
    """人脸识别数据管理类，负责加载和更新人脸数据"""
    face_data_updated = pyqtSignal()  # 人脸数据更新信号

    def __init__(self, db_path='employees.db'):
        super().__init__()
        self.db = EmployeeDB(db_path)
        self.known_face_encodings = []
        self.known_face_names = []
        self.known_face_employee_ids = []

        # 初始加载人脸数据
        self.reload_face_data()

    def reload_face_data(self):
        """重新加载所有员工的人脸数据到内存"""
        try:
            # 获取所有员工数据
            employees = self.db.get_all_employees()

            # 清空当前内存数据
            self.known_face_encodings.clear()
            self.known_face_names.clear()
            self.known_face_employee_ids.clear()

            # 处理每位员工的人脸数据
            for employee in employees:
                if employee.get("face_encoding") is not None:
                    try:
                        # 将二进制数据转换为 numpy 数组
                        face_encoding = np.frombuffer(employee["face_encoding"], dtype=np.float64)

                        # 添加到已知人脸列表
                        self.known_face_encodings.append(face_encoding)
                        self.known_face_names.append(employee["name"])
                        self.known_face_employee_ids.append(employee["employee_id"])
                    except Exception as e:
                        print(f"加载员工 {employee['name']} 的人脸数据时出错: {str(e)}")

            print(f"成功加载 {len(self.known_face_encodings)} 位员工的人脸数据")

            # 发送数据更新信号
            self.face_data_updated.emit()

        except Exception as e:
            print(f"加载人脸数据时发生错误: {str(e)}")

    def get_face_data(self):
        """返回当前的人脸数据"""
        return self.known_face_encodings, self.known_face_names, self.known_face_employee_ids


class CameraWindow(MainWindow):
    def __init__(self, parent=None):
        super(CameraWindow, self).__init__(parent)
        # 创建人脸识别管理器
        self.face_manager = FaceRecognitionManager()

        # 初始化摄像头
        self.init_camera()

        # 初始化人脸识别参数
        self.init_face_recognition()

        # face manager中reload face data发送信号，将触发update_face_data 调用
        # 通知当前对象的，到face manager去取数据
        self.face_manager.face_data_updated.connect(self.update_face_data)

        # 设置视频标签自适应但限制大小，防止窗口无限扩大
        self.setup_video_label()

        # 设置定时器用于更新摄像头画面
        self.video_timer = QTimer(self)
        self.video_timer.timeout.connect(self.update_camera_frame)
        self.video_timer.start(30)  # 约30ms一帧

        # 设置初始状态
        self.reset_ui_to_initial()

        # 添加重置定时器
        self.reset_timer = QTimer(self)
        # 超时触发
        self.reset_timer.timeout.connect(self.reset_ui_to_initial)
        # 设置为单次触发模式
        self.reset_timer.setSingleShot(True)

        # 考勤状态跟踪
        self.last_attendance_time = time.time()  # 记录上次打卡时间
        self.is_showing_result = False  # 标记当前是否正在显示打卡结果
        self.in_admin_mode = False  # 标记是否在管理界面

    def reset_ui_to_initial(self):
        """重置UI到初始状态"""
        # 隐藏所有用户信息控件
        self.user_name.hide()
        self.user_id.hide()
        self.sign_time.hide()
        self.sign_status.hide()
        self.avatar_container.hide()

        # 设置签到状态为准备就绪
        self.sign_status.setText("准备就绪")
        self.sign_status.setStyleSheet("""
            QLabel#sign_status {
                font: bold 28pt "微软雅黑";
                color: #5ce1b6;
                text-align: center;
            }
        """)
        self.sign_status.show()

        # 重置显示状态
        self.is_showing_result = False

    def setup_video_label(self):
        """设置视频标签限制大小，防止窗口无限扩大"""
        self.video_label.setMinimumSize(640, 480)
        self.video_label.setMaximumSize(3840, 2160)  # 限制最大尺寸

    def init_camera(self):
        """初始化摄像头"""
        # 打开索引为0的默认摄像头
        self.video_capture = cv2.VideoCapture(0)
        self.no_video = False
        self.current_frame = None

    def init_face_recognition(self):
        """初始化人脸识别相关参数"""
        # 人脸识别处理参数
        self.face_locations = []
        self.face_encodings = []
        self.face_names = []
        self.process_this_frame = True
        self.tolerance = 0.5  # 人脸匹配的容忍度
        self.facefx = self.facefy = 0.25  # 人脸检测缩放比例
        self.facescale = 4  # 缩放倍数

        # 获取人脸识别数据（从管理器）
        self.update_face_data()

        # 初始化中文字体
        self.font = None
        try:
            self.font = ImageFont.truetype("simhei.ttf", 24)  # 黑体
        except:
            try:
                # 尝试系统字体
                self.font = ImageFont.truetype("Arial", 24)
            except:
                # 如果找不到任何字体，使用默认字体
                print("警告：无法加载字体，使用默认字体")
                self.font = ImageFont.load_default()

    def update_face_data(self):
        """从人脸管理器更新人脸数据"""
        self.known_face_encodings, self.known_face_names, self.known_face_employee_ids = self.face_manager.get_face_data()

    def update_camera_frame(self):
        """更新摄像头画面并识别人脸"""
        try:
            # 判断摄像头是否可用
            if self.no_video:
                self.video_capture = cv2.VideoCapture(0)
                self.no_video = False

            # 摄像头读取一帧画面
            # ret：布尔值，表示读取是否成功
            # frame：捕获的视频帧（图像数据）
            ret, frame = self.video_capture.read()
            if not ret:
                self.no_video = True
                return

            # 画面水平翻转
            frame = cv2.flip(frame, 1)

            # 进行人脸识别和处理
            processed_frame = self.process_frame(frame)

            # 显示到UI
            self.display_frame(processed_frame)

            # 保存当前帧，供拍照功能使用
            self.current_frame = frame.copy()  # 关键：保存当前帧

        except Exception as e:
            print(f"处理摄像头帧时出错: {e}")
            # 尝试重新初始化摄像头
            self.no_video = True

    def process_frame(self, frame):
        """处理视频帧，识别人脸并绘制识别框"""
        try:
            # 如果当前在管理界面，跳过人脸识别
            if self.in_admin_mode:
                return frame

            # 缩小帧尺寸以加快处理速度
            small_frame = cv2.resize(frame, (0, 0), fx=self.facefx, fy=self.facefy)

            # 将图像从BGR转换为RGB
            # 将OpenCV的图像格式转换为face_recognition库需要的格式
            rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)

            # 仅处理每隔一帧，以节省处理时间
            if self.process_this_frame:
                # 检测所有人脸位置和人脸编码，可能检测到多张人脸，因此返回的是集合，每个元素是一张人脸信息
                self.face_locations = face_recognition.face_locations(rgb_small_frame)
                self.face_encodings = face_recognition.face_encodings(rgb_small_frame,
                                                                      self.face_locations)

                self.face_names = []
                for face_encoding in self.face_encodings:
                    # 检查人脸是否与已知人脸匹配
                    matches = face_recognition.compare_faces(
                        self.known_face_encodings,
                        face_encoding,
                        tolerance=self.tolerance
                    )
                    name = "未知人员"

                    # 如果找到匹配项，使用第一个匹配项的名称
                    if True in matches:
                        first_match_index = matches.index(True)
                        name = self.known_face_names[first_match_index]

                        # 处理打卡逻辑
                        self.handle_attendance(
                            self.known_face_employee_ids[first_match_index],
                            name
                        )

                    self.face_names.append(name)

            self.process_this_frame = not self.process_this_frame

            # 在检测到的人脸周围绘制框和标签
            # face_locations 每一个元素是一个4个数字的元组，表示在frame图像中的坐标，单位像素
            # frame图像原点位置 左上角 (0,0)
            # x轴：水平向右增加
            # y轴：垂直向下增加
            # face_locations中的四个数值 (top, right, bottom, left)
            # top y最小值，上边界位置， bottom y最大值，下边界位置
            # left x最小值，左边界位置， right x最大值，右边界位置
            # zip将数组展开，每张脸循环一次，绘制框
            for (top, right, bottom, left), name in zip(self.face_locations, self.face_names):
                # 缩放坐标到原始尺寸
                top = int(top * self.facescale)
                right = int(right * self.facescale)
                bottom = int(bottom * self.facescale)
                left = int(left * self.facescale)

                # 确保坐标在图像范围内
                # frame.shape  (高度, 宽度, 通道数)，取前两项
                height, width = frame.shape[:2]
                # 确保没有负数
                top = max(0, min(top, height - 1))
                left = max(0, min(left, width - 1))
                bottom = max(0, min(bottom, height - 1))
                right = max(0, min(right, width - 1))

                # 计算框的1/5宽度作为线段长度
                segment_length = min(int((right - left) / 5), 100)  # 限制最大长度

                # 绘制四个角的白线 - 保留原有样式
                color = (255, 255, 255)  # 白色
                thickness = 2  # 线宽

                # 左上角
                cv2.line(frame, (left, top), (left + segment_length, top), color, thickness)  # 上横线
                cv2.line(frame, (left, top), (left, top + segment_length), color, thickness)  # 左竖线

                # 右上角
                cv2.line(frame, (right, top), (right - segment_length, top), color, thickness)  # 上横线
                cv2.line(frame, (right, top), (right, top + segment_length), color, thickness)  # 右竖线

                # 左下角
                cv2.line(frame, (left, bottom), (left + segment_length, bottom), color, thickness)  # 下横线
                cv2.line(frame, (left, bottom), (left, bottom - segment_length), color, thickness)  # 左竖线

                # 右下角
                cv2.line(frame, (right, bottom), (right - segment_length, bottom), color, thickness)  # 下横线
                cv2.line(frame, (right, bottom), (right, bottom - segment_length), color, thickness)  # 右竖线

                # 在框下方绘制名称标签（透明背景）
                # 添加中文文本 - 使用PIL渲染中文，透明背景
                frame = self.cv2_add_chinese_text(
                    frame,
                    name,
                    (left + 10, bottom + 10),  # 将文字放在框的下方
                    text_color=(255, 255, 255),  # 白色文字
                    font_size=20  # 适当增大字号
                )
        except Exception as e:
            print(f"处理人脸识别时出错: {e}")

        return frame

    def cv2_add_chinese_text(self, img, text, position, text_color=(255, 255, 255), font_size=24):
        """在OpenCV图像上添加中文文本（透明背景）"""
        try:
            # 如果已经是PIL图像，直接使用
            if isinstance(img, Image.Image):
                pil_img = img
            else:
                # 将OpenCV图像转换为PIL图像
                img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                pil_img = Image.fromarray(img_rgb)

            # 创建绘图对象
            draw = ImageDraw.Draw(pil_img, 'RGBA')  # 使用RGBA模式支持透明度

            # 如果字体未初始化，尝试初始化
            if self.font is None:
                self.init_face_recognition()

            # 确保有可用的字体
            font = self.font
            if font_size != 24 and self.font.path:  # 如果字体文件存在
                try:
                    font = ImageFont.truetype(self.font.path, font_size)
                except:
                    font = self.font

            # 在主位置绘制文本
            draw.text(position, text, font=font, fill=(*text_color, 255))  # 完全不透明

            # 将PIL图像转换回OpenCV格式
            return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
        except Exception as e:
            print(f"添加中文文本时出错: {e}")
            return img  # 出错时返回原图像

    def display_frame(self, frame):
        """显示帧到UI，基于容器大小缩放"""
        try:
            # 转换颜色空间 BGR -> RGB
            rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            # 创建QImage
            h, w, ch = rgb_frame.shape
            bytes_per_line = ch * w
            qt_image = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)

            # 创建QPixmap
            pixmap = QPixmap.fromImage(qt_image)

            # 获取视频标签的大小，等比例缩放到合适大小
            container_size = self.video_label.size()
            scaled_pixmap = pixmap.scaled(
                container_size.width(), container_size.height(),
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation
            )

            # 设置到标签
            self.video_label.setPixmap(scaled_pixmap)
        except Exception as e:
            print(f"显示帧时出错: {e}")

    def handle_attendance(self, employee_id, name):
        """处理考勤打卡逻辑"""
        # 如果当前在管理界面，跳过打卡
        if self.in_admin_mode:
            return

        # 如果正在显示结果，则跳过
        if self.is_showing_result:
            return

        current_time = time.time()

        # 记录打卡时间
        self.last_attendance_time = current_time

        # 更新UI显示考勤信息
        self.update_attendance_ui(name, employee_id)

        # 记录考勤到数据库（这里简化为打印信息）
        sign_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(current_time))
        print(f"员工 {name} ({employee_id}) 已签到，时间: {sign_time}")

    def update_attendance_ui(self, name, employee_id):
        """更新UI显示考勤信息"""
        # 设置正在显示结果的标志
        self.is_showing_result = True

        # 隐藏提示标签，准备就绪 的文字
        if hasattr(self, 'prompt_label'):
            self.prompt_label.hide()

        # 显示用户信息控件
        self.user_name.show()
        self.user_id.show()
        self.sign_time.show()
        self.sign_status.show()
        self.avatar_container.show()

        # 设置用户信息
        self.user_name.setText(name)
        self.user_id.setText(f"工号: {employee_id}")

        # 设置签到时间（当前时间）
        current_time = time.strftime("%H:%M", time.localtime())
        self.sign_time.setText(f"签到时间: {current_time}")

        # 更新签到状态
        self.sign_status.setText("签到成功!")

        # 查找并显示员工头像
        employees = self.face_manager.db.get_all_employees()
        for emp in employees:
            if emp["employee_id"] == employee_id:
                avatar_path = emp["avatar"]
                if os.path.exists(avatar_path):
                    self.set_user_avatar(avatar_path)
                break

        # 确保用户信息界面可见
        if self.admin_container.isVisible():
            self.switch_to_user_view()

        # 5秒后重置UI，启动倒计时
        self.reset_timer.start(5000)  # 2000毫秒=2秒

    # 覆盖父类方法，添加管理界面状态标记
    def switch_to_admin_view(self):
        """切换到管理界面"""
        super().switch_to_admin_view()
        self.in_admin_mode = True  # 设置为管理界面状态

    def switch_to_user_view(self):
        """切换回用户信息界面"""
        super().switch_to_user_view()
        self.in_admin_mode = False  # 退出管理界面状态

    def closeEvent(self, event):
        """关闭窗口时释放摄像头"""
        try:
            if hasattr(self, 'video_capture'):
                self.video_capture.release()
        except Exception as e:
            print(f"关闭摄像头时出错: {e}")
        event.accept()


if __name__ == "__main__":
    # 创建应用
    app = QApplication(sys.argv)

    # 设置全局样式
    app.setStyle("Fusion")
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(10, 20, 40))
    palette.setColor(QPalette.WindowText, QColor(200, 220, 255))
    app.setPalette(palette)

    # 创建主窗口
    window = CameraWindow()
    # 设置窗口最大化
    window.showMaximized()
    window.show()

    # 运行应用
    sys.exit(app.exec_())
